比较运算符
JavaScript中存在两种比较运算符,分别是== 和 === 两种:
第一种是’==’比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是’===’比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用 == 比较,始终坚持使用 === 比较。
NaN
另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:1
NaN === NaN; // false
唯一能判断NaN的方法是通过isNaN()函数:
1 | isNaN(NaN); // true |
null、undefined
- null
null表示一个空值,是一种广义上的“空”,是类型无关的;
和null比较,0表示的是一个数值;空字符’ ‘表示的长度为0的字符串;
- undefined
表示未定义,也有“空”的意思,大多数情况用来表示一个函数的参数没有被传递过来;
数组
JavaScript中的数组起着类似于其他语言中集合的作用,其提供了许多操作结合的函数;
JavaScript中的数组是类型无关的,其中可以包含任意的数据类型。
1 | [1, 2, 3.14, 'Hello', null, true]; |
另一种定义数组的方式:1
new Array(1, 2, 3); // 创建了数组[1, 2, 3]
array.length可以用来获取数组的长度,同时,还可以通过给array.length赋值来改变数组的长度:1
2
3
4
5
6
7
8
9
10
11var arr = [1, 2, 3];
arr.length; // 3
arr.length = 6;
arr; // arr变为[1, 2, 3, undefined, undefined, undefined]
arr.length = 2;
arr; // arr变为[1, 2]
// 访问数组越界也会导致数组的大小改变
var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
数组操作
- push、pop实现数组尾部的增删操作,push支持一次性操作多个元素;
- unshift、shift实现数组头部元素的增删操作,unshift支持多个元素的操作;
- slice对应了字符串中的subString操作,直接调用slice()函数可以方便的复制整个数组;
splice可以直接对数据某个位置上的元素进行增删;
1
2
3
4
5
6
7
8
9
10var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']join()能够用制定的连接符将数组中的所有元素连接起来;
Map、Set集合
ES6新增了Map和Set集合结果;
- Map
Map如果直接初始化,需要使用一个二维数组进行初始化:1
2var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
直接设置:1
2
3
4
5
6
7var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
- Set可以理解为没有重复元素的数组
1 | s.add(4); |
- for..of
for..of是ES6引进来的遍历Array、Map和Set类型集合的循环,不同于for..in,for..of只遍历原始集合中数据,而对于动态添加的数据不进行遍历:
1 | // for .. in 原先是用来遍历对象的所有属性名的,而Array可以认为是一个只有属性名,没有属性值的对象 |
1 | var a = ['A', 'B', 'C']; |
对象
JavaScript的对象是由一组键-值组成的无序集合(类似于Python中的字典):
1 | var person = { |
其中的键值对的键又称为对象的属性,都是字符串类型,值则可以是任意类型;
对象直接直接添加或删除属性
1
2
3
4
5
6
7
8
9
10
11var xiaoming = {
name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错判断是否拥有某属性
in可以用来判断对象是否拥有某属性,包含了继承的属性:1
2
3
4
5
6
7
8
9
10var xiaoming = {
name: '小明',
birth: 1990,
school: 'No.1 Middle School',
height: 1.70,
weight: 65,
score: null
};
'name' in xiaoming; // true
'grade' in xiaoming; // false
hasOwnProperty用来判断某对象是否自带某属性,不包含继承的:1
2
3
4
5var xiaoming = {
name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
- for..in遍历对象或数组属性
1
2
3
4
5
6
7
8var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
console.log(key); // 'name', 'age', 'city'
}
1 | var a = ['A', 'B', 'C']; |
方法
函数
arguments是函数内部的关键字,可以用来获取传给函数的所有参数,
1
2
3
4
5
6
7function foo(x) {
console.log('x = ' + x); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);rest参数
传递给函数的参数并不需要完全参照函数定义个参数个数来确定,我们可以传递任意个数的参数给函数;
对于额外传递给函数的参数,我们可以通过rest参数来获取(ES6引入);
1 | function foo(a, b, ...rest) { |
命名空间
- 在JavaScript中,凡是没有定义在函数中的变量或者函数,都是全局变量;
全局变量缔属于window对象,所有的全局变量或者函数都可以使用window对象来调起;
1
2
3
4
5
6
7
8'use strict';
function foo() {
alert('foo');
}
foo(); // 直接调用foo()
window.foo(); // 通过window.foo()调用因为不同的JavaScript文件可能定义了相同名字的全局变量或者函数,这将导致冲突,为了解决冲突,我们可以为每一个JavaScript文件定义一个命名空间;
1
2
3
4
5
6
7
8
9
10
11// 唯一的全局变量MYAPP:
var MYAPP = {};
// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;
// 其他函数:
MYAPP.foo = function () {
return 'foo';
};
局部作用域
在JavaScript中,局部作用域一般是只有函数内部的局部作用,而不像Java等,存在块级的作用域:1
2
3
4
5
6
7
8'use strict';
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用变量i
}
ES6引入新的let关键字,用来替代var,可以用来声明一个块级的作用域(代码块!)1
2
3
4
5
6
7
8
9
10'use strict';
function foo() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
// SyntaxError:
i += 1;
}
strict模式与全局变量
在JavaScript中,变量不同过var修饰,则为全局变量;
如果多个JavaScript文件中存在同名的全局变量,那么将会带来不可预知的错误;
解决方案是使用strict模式:只需要在JavaScript代码的第一行添加:1
'use strict';
那么支持strict模式的浏览器将强制用var声明变量,否则将报ReferenceError错误;
字符串操作
JavaScript中字符串是一个常量,对其任何操作都不会改变其原内容,而是返回一个新的字符串结果。
字符串可以认为是一串字符数组;
- 多行字符串
新的ES6标准允许我们直接使用回车作为换行符:使用反引号...
进行括起;
1 | `这是一个 |
- 字符串连接
在JavaScript中,多个字符串要连接成一个字符串,可以使用“+”进行连接;
在ES6中,支持我们使用字符串模板的形式进行连接(必须使用反引号来括起内容):1
2
3
4var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);
ES6新特性
常量:可以使用const来声明一个常量;
1
2
3
4
5'use strict';
const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
PI; // 3.14解构赋值:将数组的元素同时赋给多个变量;
1
2// 如果浏览器支持解构赋值就不会报错:
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
解构赋值可以进行灵活的运用,比如对二维数组进行解构:
1 | let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']]; |
忽略部分元素,只解构出一部分元素:
1 | let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素 |
- 解构对象:解构对象需要使用对应的属性名,并且用大括号括起来;
1
2
3
4
5
6
7
8
9
10'use strict';
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
var {name, age, passport} = person;
同样,对于嵌套的对象,也可以解构出其嵌套值;
1 | var person = { |
解构对象时,并不一定要于对象的的属性名一致,可以另外指定其他变量来接收解构的属性:
1 | var person = { |
可以给要获取解构的属性的变量设置默认值:1
2
3
4
5
6
7
8
9
10
11var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678'
};
// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
name; // '小明'
single; // true
- 解构对象时注意点
对于已经声明的变量,用它们来接收解构属性时,需要用括号括起,表示是一个运行语句,而不是代码块:
1 | // 声明变量: |
因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。1
2正确写法:
({x, y} = { name: '小明', x: 100, y: 200});
解构赋值使用场景
- 方便交换变量内容
1 | var x=1, y=2; |
解构对象内容
1
var {hostname:domain, pathname:path} = location;
解构作为函数参数的参数,实现具名参数和默认参数
1
2
3
4
5
6
7
8// 解构时可以设置默认值
function buildDate({year, month, day, hour=0, minute=0, second=0}) {
return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
// 传递的对象的属性顺序并不要求是固定的
buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)